import random
import time
from datetime import datetime
from typing import Callable
from .error_types import ErrorType, ErrorContext, RetryConfig, RetryStrategy

class RetryDecisionEngine:
    """重试决策引擎"""
    
    # 每种错误类型的默认重试配置
    DEFAULT_RETRY_CONFIGS = {
        ErrorType.NETWORK_ERROR: RetryConfig(
            strategy=RetryStrategy.EXPONENTIAL,
            max_attempts=5,
            base_delay=2.0,
            max_delay=120.0
        ),
        ErrorType.API_RATE_LIMIT: RetryConfig(
            strategy=RetryStrategy.EXPONENTIAL,
            max_attempts=8,
            base_delay=5.0,
            max_delay=300.0
        ),
        ErrorType.SERVICE_UNAVAILABLE: RetryConfig(
            strategy=RetryStrategy.EXPONENTIAL,
            max_attempts=4,
            base_delay=3.0,
            max_delay=60.0
        ),
        ErrorType.TIMEOUT: RetryConfig(
            strategy=RetryStrategy.LINEAR,
            max_attempts=3,
            base_delay=5.0,
            max_delay=30.0
        ),
        ErrorType.UNKNOWN_ERROR: RetryConfig(
            strategy=RetryStrategy.EXPONENTIAL,
            max_attempts=2,
            base_delay=1.0,
            max_delay=10.0
        ),
        # 不重试的错误类型
        ErrorType.AUTH_ERROR: RetryConfig(strategy=RetryStrategy.NO_RETRY),
        ErrorType.QUOTA_EXCEEDED: RetryConfig(strategy=RetryStrategy.NO_RETRY),
        ErrorType.VALIDATION_ERROR: RetryConfig(strategy=RetryStrategy.NO_RETRY),
        ErrorType.CONTENT_FILTER: RetryConfig(strategy=RetryStrategy.NO_RETRY),
    }
    
    @classmethod
    def should_retry(cls, error_context: ErrorContext) -> bool:
        """判断是否应该重试"""
        
        retry_config = cls.DEFAULT_RETRY_CONFIGS.get(
            error_context.error_type,
            cls.DEFAULT_RETRY_CONFIGS[ErrorType.UNKNOWN_ERROR]
        )
        
        # 检查重试策略
        if retry_config.strategy == RetryStrategy.NO_RETRY:
            return False
        
        # 检查重试次数
        if error_context.attempt_number >= retry_config.max_attempts:
            return False
        
        # 检查总时间限制（防止无限重试）
        total_time = (datetime.now() - error_context.start_time).total_seconds()
        if total_time > 1800:  # 30分钟限制
            return False
        
        return True
    
    @classmethod
    def calculate_delay(cls, error_context: ErrorContext) -> float:
        """计算重试延迟时间"""
        
        retry_config = cls.DEFAULT_RETRY_CONFIGS.get(
            error_context.error_type,
            cls.DEFAULT_RETRY_CONFIGS[ErrorType.UNKNOWN_ERROR]
        )
        
        if retry_config.strategy == RetryStrategy.NO_RETRY:
            return 0
        
        elif retry_config.strategy == RetryStrategy.IMMEDIATE:
            delay = 0
        
        elif retry_config.strategy == RetryStrategy.LINEAR:
            delay = retry_config.base_delay * error_context.attempt_number
        
        elif retry_config.strategy == RetryStrategy.EXPONENTIAL:
            delay = retry_config.base_delay * (retry_config.backoff_multiplier ** (error_context.attempt_number - 1))
        
        elif retry_config.strategy == RetryStrategy.ADAPTIVE:
            # 自适应策略：根据历史成功率调整
            delay = cls._adaptive_delay(error_context, retry_config)
        
        else:
            delay = retry_config.base_delay
        
        # 应用最大延迟限制
        delay = min(delay, retry_config.max_delay)
        
        # 添加随机抖动
        if retry_config.jitter and delay > 0:
            jitter_range = delay * 0.1  # 10%的随机抖动
            delay += random.uniform(-jitter_range, jitter_range)
            delay = max(0, delay)  # 确保不为负数
        
        return delay
    
    @classmethod
    def _adaptive_delay(cls, error_context: ErrorContext, retry_config: RetryConfig) -> float:
        """自适应延迟计算"""
        # 基于历史重试延迟调整
        if hasattr(error_context, 'retry_delays') and error_context.retry_delays:
            avg_delay = sum(error_context.retry_delays) / len(error_context.retry_delays)
            # 如果之前的延迟都很短但仍失败，则增加延迟
            return avg_delay * 1.5
        else:
            return retry_config.base_delay


class CircuitBreaker:
    """断路器模式实现"""
    
    def __init__(self, failure_threshold: int = 5, recovery_timeout: int = 60):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = "CLOSED"  # CLOSED, OPEN, HALF_OPEN
    
    def call(self, func: Callable, *args, **kwargs):
        """通过断路器调用函数"""
        
        if self.state == "OPEN":
            if self._should_attempt_reset():
                self.state = "HALF_OPEN"
            else:
                raise Exception("Circuit breaker is OPEN - service unavailable")
        
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise e
    
    def _should_attempt_reset(self) -> bool:
        """检查是否应该尝试重置"""
        if self.last_failure_time is None:
            return True
        
        time_since_failure = time.time() - self.last_failure_time
        return time_since_failure >= self.recovery_timeout
    
    def _on_success(self):
        """成功时的处理"""
        self.failure_count = 0
        self.state = "CLOSED"
    
    def _on_failure(self):
        """失败时的处理"""
        self.failure_count += 1
        self.last_failure_time = time.time()
        
        if self.failure_count >= self.failure_threshold:
            self.state = "OPEN"
    
    def get_state(self) -> str:
        """获取当前状态"""
        return self.state
    
    def reset(self):
        """手动重置断路器"""
        self.failure_count = 0
        self.last_failure_time = None
        self.state = "CLOSED"